package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.sql.Array;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private VodConstantProperties props;

    @Autowired
    private VodService vodService;

    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * 内容创作者、运营人员保存声音
     *
     * @param userId      当前登录用户ID
     * @param trackInfoVo 声音VO对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo) {
        //1. 封装声音VO对象 保存声音
        //1.1 将提交声音VO转为PO对象
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //1.2 手动为声音对象赋值 用户ID、声音来源、状态、声音序号、媒体文件详情
        trackInfo.setUserId(userId);
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, trackInfoVo.getAlbumId());
        Long count = trackInfoMapper.selectCount(queryWrapper);
        trackInfo.setOrderNum(count.intValue() + 1);
        //1.3 调用腾讯云点播获取媒体文件详情接口获取 声音时长、声音类型
        TrackMediaInfoVo mediaInfoVo = vodService.getTrackMediaInfo(trackInfo.getMediaFileId());
        if (mediaInfoVo != null) {
            trackInfo.setMediaType(mediaInfoVo.getType());
            trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfoVo.getDuration()));
            trackInfo.setMediaSize(mediaInfoVo.getSize());
        }
        //1.4 保存声音
        trackInfoMapper.insert(trackInfo);

        //2.初始化保存声音统计信息
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);

        //3.为声音所属专辑包含声音数量+1
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);
    }

    /**
     * 新增声音统计信息
     *
     * @param trackId
     * @param statType
     */
    @Override
    public void saveTrackStat(Long trackId, String statType) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(0);
        trackStatMapper.insert(trackStat);
    }

    /**
     * 根据条件分页查询当前登录用户声音列表
     *
     * @param pageInfo       MP分页对象
     * @param trackInfoQuery 查询条件
     * @return
     */
    @Override
    public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.getUserTrackPage(pageInfo, trackInfoQuery);
    }

    /**
     * 修改声音
     *
     * @param id          声音ID
     * @param trackInfoVo 修改后声音信息
     * @return
     */
    @Override
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        //1.判断声音文件是否变更，如果变更更新声音媒体文件信息（类型、大小、时长）
        //1.1 根据声音ID查询声音对象
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        String orginalMediaFileId = trackInfo.getMediaFileId();
        BeanUtil.copyProperties(trackInfoVo, trackInfo);
        //1.2 查询数据库中声音唯一标识跟提交VO对象中唯一标识比对
        //1.3 如果不一致说明变更，调用腾讯云点播接口获取变更后声音信息
        if (!orginalMediaFileId.equals(trackInfoVo.getMediaFileId())) {
            //删除云点播指定音频文件
            vodService.deleteTrackMedia(orginalMediaFileId);

            TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(trackInfoVo.getMediaFileId());
            trackInfo.setMediaType(trackMediaInfo.getType());
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfo.getSize());
        }
        //2.根据主键更新声音信息
        trackInfoMapper.updateById(trackInfo);
    }

    /**
     * 删除声音
     * <p>
     * 1.更新序号大于删除声音列表，更新序号 对序号减一
     * 2.删除声音记录
     * 3.删除声音统计记录
     * 4.更新专辑包含声音数量
     * 5.调用腾讯云点播平台将声音音频文件删除
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1.更新序号大于删除声音列表，更新序号 对序号减一
        //1.1 先获取删除声音信息 获取序号
        TrackInfo trackInfo = trackInfoMapper.selectById(id);

        //1.2 批量更新比当前声音序号大声音序号值
        trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(), trackInfo.getOrderNum());

        //2.删除声音记录
        trackInfoMapper.deleteById(id);

        //3.删除声音统计记录
        LambdaQueryWrapper<TrackStat> trackStatLambdaQueryWrapper = new LambdaQueryWrapper<>();
        trackStatLambdaQueryWrapper.eq(TrackStat::getTrackId, id);
        trackStatMapper.delete(trackStatLambdaQueryWrapper);

        //4.更新专辑包含声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);

        //5.调用腾讯云点播平台将声音音频文件删除
        vodService.deleteTrackMedia(trackInfo.getMediaFileId());
    }

    /**
     * 用于小程序端专辑页面展示分页声音列表，动态根据用户展示声音付费标识
     *
     * @param pageInfo MP分页对象
     * @param albumId  专辑ID
     * @param userId   用户ID
     * @return
     */
    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long albumId, Long userId) {
        //1.根据专辑ID查询该专辑下包含声音列表-默认查询到声音：不需要付费
        pageInfo = trackInfoMapper.getAlbumTrackPage(pageInfo, albumId);
        //1.1 获取到当前页中声音列表
        List<AlbumTrackListVo> trackList = pageInfo.getRecords();
        //1.2 根据专辑ID查询专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //付费类型: 0101-免费、0102-vip免费、0103-付费
        String payType = albumInfo.getPayType();
        //TODO:处理付费标识 找出哪些需要付费或者进一步验证购买情况
        //2.处理未登录情况，获取到专辑信息得到专辑价格类型 VIP免费/付费 将除了免费试听以外的声音全部设置付费标识
        if (userId == null) {
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //采用Stream流过滤，将声音列表中序号大于专辑中设置免费试听集数（数值）全部将付费标识改为true
                trackList.stream()
                        .filter(trackInfo -> trackInfo.getOrderNum() > albumInfo.getTracksForFree()) //过滤找出需要付费声音
                        .collect(Collectors.toList()) //收集为集合
                        .stream().forEach(trackInfo -> {  //再次对过滤后集合中元素遍历
                            //修改付费标识
                            trackInfo.setIsShowPaidMark(true);
                        });
            }
        } else {
            //3.处理已登录情况
            boolean isNeedCheckPaySatue = false;
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVoById(userId).getData();
            //3.1 远程调用用户服务获取用户信息，得到用户VIP状态是否过期
            Integer isVip = userInfoVo.getIsVip();
            //3.2 判断专辑付费类型如果是VIP免费或者付费 且登录用户为普通用户或者VIP会员过期 进一步判断用户购买情况
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //3.2.1 如果是普通用户
                if (isVip.intValue() == 0) {
                    isNeedCheckPaySatue = true;
                }
                //3.2.2 如果是VIP会员但是会员过期
                if (isVip.intValue() == 1 && new Date().after(userInfoVo.getVipExpireTime())) {
                    isNeedCheckPaySatue = true;
                }
            }

            //3.3 判断专辑付费类型如果是付费 无论普通用户或者VIP会员 进一步判断用户购买情况
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                isNeedCheckPaySatue = true;
            }
            //3.4 统一集中处理需要进一步判断用户购买情况，调用用户微服务得到本页中声音购买情况
            if (isNeedCheckPaySatue) {
                //3.5 将本页中需要校验声音ID获取到（去掉免费试听声音ID）调用用户服务得到本页中声音购买情况
                List<AlbumTrackListVo> needCheckTrackList =
                        trackList.stream()
                                .filter(trackInfo -> trackInfo.getOrderNum() > albumInfo.getTracksForFree())
                                .collect(Collectors.toList());

                List<Long> needCheckTrackIdList =
                        needCheckTrackList.stream()
                                .map(AlbumTrackListVo::getTrackId)
                                .collect(Collectors.toList());
                Map<Long, Integer> buyStatusMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckTrackIdList).getData();
                //3.6 根据用户服务响应购买情况，如果用户未购买，为声音设置付费标识：true
                for (AlbumTrackListVo albumTrackListVo : needCheckTrackList) {
                    if (buyStatusMap.get(albumTrackListVo.getTrackId()) == 0) {
                        //用户未购买声音，显示付费标识
                        albumTrackListVo.setIsShowPaidMark(true);
                    }
                }
            }
        }
        return pageInfo;
    }


    /**
     * 更新声音及专辑统计信息
     *
     * @param mqVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackStat(TrackStatMqVo mqVo) {
        //1.更新声音统计信息（0701-播放量 0702-收藏量 0703-点赞量 0704-评论数）
        trackInfoMapper.updateStat(mqVo.getTrackId(), mqVo.getStatType(), mqVo.getCount());


        //2.更新专辑统计信息（0401-播放量 0402-订阅量 0403-购买量 0404-评论数'）
        if (SystemConstant.TRACK_STAT_PLAY.equals(mqVo.getStatType())) {
            albumInfoMapper.updateStat(mqVo.getAlbumId(), SystemConstant.ALBUM_STAT_PLAY, mqVo.getCount());
        }
        if (SystemConstant.TRACK_STAT_COMMENT.equals(mqVo.getStatType())) {
            albumInfoMapper.updateStat(mqVo.getAlbumId(), SystemConstant.ALBUM_STAT_COMMENT, mqVo.getCount());
        }
    }

    /**
     * 根据声音ID查询声音统计信息
     *
     * @param trackId
     * @return
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        return trackInfoMapper.getTrackStatVo(trackId);
    }


    /**
     * 提供给小程序端，用户点击某个待购声音后，展示待购分集对象
     *
     * @param trackId 选择预购声音ID
     * @return 【{"name":"本集",price:1, trackCount:1},{"name":"后10集",price:10, trackCount:10}】
     */
    @Override
    public List<Map<String, Object>> getUserWaitBuyTrackPayList(Long userId, Long trackId) {
        //1.根据入参中声音ID查询待购声音对象 得到序号及专辑ID
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);

        //2.根据声音所属专辑ID+序号 获取大于当前声音序号所有待购声音列表
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
        queryWrapper.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        List<TrackInfo> userWaitBuyTrackList = trackInfoMapper.selectList(queryWrapper);

        //3.远程调用“用户服务”获取当前用户已购声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(trackInfo.getAlbumId()).getData();

        //4.将待购声音列表中包含已购买的声音排除掉
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            userWaitBuyTrackList = userWaitBuyTrackList.stream()
                    .filter(waitBuyTrackInfo -> !userPaidTrackIdList.contains(waitBuyTrackInfo.getId()))
                    .collect(Collectors.toList());
        }
        //5.根据剩余待购声音列表长度动态构建分集购买对象
        List<Map<String, Object>> mapArrayList = new ArrayList<>();
        //5.1 构建本集分集购买对象
        //5.2 获取声音单价
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        BigDecimal price = albumInfo.getPrice();
        Map<String, Object> currJiMap = new HashMap<>();
        currJiMap.put("name", "本集");
        currJiMap.put("price", price);
        currJiMap.put("trackCount", 1);
        mapArrayList.add(currJiMap);
        //5.3 剩余待购集为8 后8集/全集   剩余待购集为15 显示：后10集  全集     剩余待购集为25 显示：后10集  后20集 全集
        int size = userWaitBuyTrackList.size();
        for (int i = 10; i <= 50; i += 10) {
            //判断如果size(待购声音数量)>i 固定显示后i集
            if (size > i) {
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + i + "集");
                map.put("price", price.multiply(BigDecimal.valueOf(i)));
                map.put("trackCount", i);
                mapArrayList.add(map);
            } else {
                //直接显示全集
                Map<String, Object> map = new HashMap<>();
                map.put("name", "全集");
                map.put("price", price.multiply(BigDecimal.valueOf(size)));
                map.put("trackCount", size);
                mapArrayList.add(map);
                break;
            }
        }
        return mapArrayList;
    }

    /**
     * 渲染订单确认页面中待购声音（商品）集合:根据选择声音ID跟数量作为条件获取待购声音列表
     *
     * @param trackId 付费标识对应声音ID
     * @param trackCount 购买数量
     * @return
     */
    @Override
    public List<TrackInfo> getUserWaitPayTrackInfoList(Long trackId, Integer trackCount) {
        Long userId = AuthContextHolder.getUserId();
        //1.根据声音ID查询声音对象，得到声音所属专辑ID
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);

        //2.远程调用“用户服务”得到该专辑已购声音ID集合
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(trackInfo.getAlbumId()).getData();

        //3.构建查询待购买声音集合（如果存在已购买声音将其排除掉）
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
        queryWrapper.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        if(CollectionUtil.isNotEmpty(userPaidTrackIdList)){
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        queryWrapper.last("limit "+trackCount);
        queryWrapper.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId);

        return trackInfoMapper.selectList(queryWrapper);
    }
}
